package model;

import java.util.ArrayList;
import java.util.Collection;

public class Node {
	
	private static int nodeCount=0;
	
	private File file;
	
	private Collection<FilePiece> completedFilePieces;
	private Collection<FilePiece> workingFilePieces;
	
	private Collection<NodeConnection> uploadConnections;
	private Collection<NodeConnection> downloadConnections;
	
	private double maxDownBandwidth;
	private double maxUpBandwidth;
	
	private double upDataSent;
	private double downDataRecieved;
	
	private Tracker tracker;
	
	private double shareRatioGoal;
	private boolean leecher;
	private boolean seeder;
	
	private boolean done;
	
	private int number;
	
	public Node(Tracker tracker, File file, double maxDownBandwidth, double maxUpBandwidth, boolean leecher, boolean seeder, double shareRatioGoal)
	{
		this.tracker = tracker;
		this.file = file;
		this.maxDownBandwidth = maxDownBandwidth;
		this.maxUpBandwidth = maxUpBandwidth;
		this.leecher = leecher;
		this.seeder = seeder;
		
		completedFilePieces = new ArrayList<FilePiece>();
		workingFilePieces = new ArrayList<FilePiece>();
		uploadConnections = new ArrayList<NodeConnection>();
		downloadConnections = new ArrayList<NodeConnection>();
		
		done=false;
		upDataSent=0;
		downDataRecieved=0;
		
		number = Node.nodeCount;
		Node.nodeCount++;
		
		if(seeder){
			for(FilePiece filePiece : file.getFilePieces()){
				completedFilePieces.add(filePiece);
			}
		}
		this.shareRatioGoal = shareRatioGoal;
	}

	public Collection<FilePiece> getCompletedFilePieces(){
		return completedFilePieces;
	}
	public Collection<FilePiece> getWorkingFilePieces(){
		return workingFilePieces;
	}
	
	public boolean isWorkingOnPiece(FilePiece filePiece){
		return workingFilePieces.contains(filePiece);
	}
	
	public boolean hasCompletedPiece(FilePiece filePiece){
		return completedFilePieces.contains(filePiece);
	}
	
	public boolean isAssociatedWithPiece(FilePiece filePiece){
		return isWorkingOnPiece(filePiece) || hasCompletedPiece(filePiece);
	}
	
	public double downBandwidthRemaining(){
		double downBandwidthRemaining = maxDownBandwidth;
		for(NodeConnection connection : downloadConnections){
			downBandwidthRemaining -= connection.getBandwidth();
		}
		return downBandwidthRemaining;
	}
	
	public double upBandwidthRemaining(){
		double upBandwidthRemaining = maxUpBandwidth;
		for(NodeConnection connection : uploadConnections){
			upBandwidthRemaining -= connection.getBandwidth();
		}
		return upBandwidthRemaining;
	}
	
	public int update(){		
		if(completedFilePieces.size()==file.getFilePieces().size()){
			if(shouldDisconnect() && !done){
				done = true;
				tracker.notifyNodeLeaving(this, true);
				return 1;
			}
		}
		else{
			if(downBandwidthRemaining()>maxDownBandwidth*.1){
				tracker.requestNewFilePiece(this);
			}
		}
		return 0;
	}
	
	public void notifyConnectionInitiated(NodeConnection nodeConnection) {
		if(nodeConnection.getUpNode()==this){
			uploadConnections.add(nodeConnection);
		} else {
			downloadConnections.add(nodeConnection);
			workingFilePieces.add(nodeConnection.getFilePiece());
		}
	}
	
	public void notifyConnectionCompleted(NodeConnection nodeConnection) {
		if(nodeConnection.getUpNode()==this){
			uploadConnections.remove(nodeConnection);
		} else {
			downloadConnections.remove(nodeConnection);
			workingFilePieces.remove(nodeConnection.getFilePiece());
			completedFilePieces.add(nodeConnection.getFilePiece());
		}
		
	}
	public void dropConnection(NodeConnection nodeConnection) {
		if(nodeConnection.getUpNode()==this){
			uploadConnections.remove(nodeConnection);
		} else {
			downloadConnections.remove(nodeConnection);
			workingFilePieces.remove(nodeConnection.getFilePiece());
		}
		
	}
	
	public void notifyDataSent(double dataSentSize){
		upDataSent += dataSentSize;
	}
	
	public void notifyDataRecived(double dataRecievedSize){
		downDataRecieved += dataRecievedSize;
	}
	
	private boolean shouldDisconnect(){
		if(leecher){
			return true;
		}
		else if(seeder){
			return false;
		}
		else if(getShareRatio()>=shareRatioGoal){
			return true;
		}
		return false;
	}
	
	public double getShareRatio(){
		return upDataSent/downDataRecieved;
	}
	
	@Override
	public String toString(){
		StringBuilder completedString = new StringBuilder();
		double dataDone = 0;
		for(FilePiece piece : completedFilePieces){
			completedString.append(piece.getNumber());
			completedString.append(" ");
			dataDone += piece.getDataSize();
		}
		for(FilePiece piece : workingFilePieces){
			//get data done for each incomplete piece
		}		
		char[] pieceStats = new char[file.getFilePieces().size()];
		
		for(int i=0; i<file.getFilePieces().size(); i++){
			pieceStats[i] = '▁';
		}
		for(FilePiece piece : workingFilePieces){
			pieceStats[piece.getNumber()] = '▄';
		}
		for(FilePiece piece : completedFilePieces){
			pieceStats[piece.getNumber()] = '█';
		}
		StringBuilder pieceStatusSB = new StringBuilder();
		for(int i=0; i< pieceStats.length; i++){
			pieceStatusSB.append(pieceStats[i]);
		}
		
		double shareRatio = getShareRatio();
		String stringRatio = Double.toString(shareRatio);
		if(!(stringRatio.equals("Infinity") || stringRatio.equals("NaN") || stringRatio.equals("0.0")))
			stringRatio = stringRatio.substring(0, (stringRatio.indexOf('.')+3 > stringRatio.length()) ? stringRatio.length() : stringRatio.indexOf('.')+3);
		if(stringRatio.equals("Infinity"))
			stringRatio = "∞";
		
		double percentComplete = dataDone/file.getFileSize() * 100;
		String stringPercent = Double.toString(percentComplete);
		if(!(stringPercent.equals("Infinity") || stringPercent.equals("NaN") || stringPercent.equals("0.0")))
			stringPercent = stringPercent.substring(0, (stringPercent.indexOf('.')+3 > stringPercent.length()) ? stringPercent.length() : stringPercent.indexOf('.')+3);
		
		double downUtlization = 1-downBandwidthRemaining()/maxDownBandwidth;
		String downPercent = Double.toString(downUtlization);
		if(!(downPercent.equals("Infinity") || downPercent.equals("NaN") || downPercent.equals("0.0")))
			downPercent = downPercent.substring(0, (downPercent.indexOf('.')+3 > downPercent.length()) ? downPercent.length() : downPercent.indexOf('.')+3);
		
		double upUtlization = 1-upBandwidthRemaining()/maxUpBandwidth;
		String upPercent = Double.toString(upUtlization);
		if(!(upPercent.equals("Infinity") || upPercent.equals("NaN") || upPercent.equals("0.0")))
			upPercent = upPercent.substring(0, (upPercent.indexOf('.')+3 > upPercent.length()) ? upPercent.length() : upPercent.indexOf('.')+3);
		
		
		String print = number + "\t" + stringRatio + "\t\t" + stringPercent + "\t\t" + pieceStatusSB.toString() + "\t" + downPercent + "\t\t" + upPercent;
		return print;
	}
	
	public int getNumber(){
		return number;
	}
	
}
